const CompressionPlugin = require('compression-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const HtmlPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const ZipPlugin = require('zip-webpack-plugin');
const fs = require('fs');
const moment = require('moment');
const path = require('path');
const webpack = require('webpack');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const ErrorOverlayPlugin = require('error-overlay-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const pkg = require('./package.json');

const context = process.cwd();
const nodeEnv = process.env.NODE_ENV || 'development'; // NODE_ENV 只能用development或production
const isProd = nodeEnv === 'production';
const productCenter = process.env.ENTRY === 'product-center';
const isDevServe = !isProd && productCenter;
const env = (process.env.ENV || 'dev').toLowerCase(); // 环境
const useSourceMap = !isProd; // env !== 'prod'; //

// 环境变量定义
const appEnv = ['sit', 'uat', 'prod'].reduce(
  (pkgEnv, key) => {
    if (env === key) {
      pkgEnv = Object.assign(pkgEnv, pkgEnv[key]);
    }
    delete pkgEnv[key];
    return pkgEnv;
  },
  { ...pkg.env, ANALYZE: process.env.ANALYZE, ENV: env },
);
Object.keys(appEnv).forEach((key) => {
  appEnv[key] = JSON.stringify(appEnv[key]);
});

// 输出文件夹名
const bundleName = pkg.bundleName || 'admin';
const packagePath = `${bundleName}-${env}`;

// 压缩包名
let zipFilename = `${packagePath}-${pkg.version}_${moment().format('YYYYMMDD')}.zip`;
(() => {
  let count = 1;
  while (fs.existsSync(path.join(context, zipFilename))) {
    zipFilename = `${packagePath}-${pkg.version}_${moment().format('YYYYMMDD')}-${count++}.zip`;
  }
})();

// 输出路径 (绝对路径)
let outputPath = path.join(context, 'product');

// 图片导入规则
const fileLoader = {
  test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
  use: [
    isProd
      ? {
          loader: 'url-loader',
          options: {
            limit: 10 * 1024, // 10KB以内解析为base64; 大于10KB输出文件
            name: 'statics/[sha256:hash:base62].[ext]',
          },
        }
      : {
          loader: 'file-loader',
        },
  ],
};

// 样式导入规则
const styleLoader = {
  test: /\.(css|less)$/i,
  use: [
    isProd
      ? {
          loader: MiniCssExtractPlugin.loader,
          options: {
            publicPath: '../',
          },
        }
      : 'style-loader',
    {
      loader: 'css-loader',
      options: {
        sourceMap: useSourceMap,
        importLoaders: 1,
        // 模块样式(css module)配置
        modules: {
          // 以.global.css,.global.less结尾的为全局样式
          // 其余样式为模块样式，只在导入使用的地方生效
          auto: /(?<!node_modules.*)(?<!global)\.(css|less)$/i,
          // 模块样式属性名转换为驼峰名
          exportLocalsConvention: 'camelCaseOnly',
          // 样式名(class)转换规则
          localIdentName: isProd ? '[hash:base64]' : '[path][name]_[local]_[hash:base64]',
        },
      },
    },
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [
            // 自动添加兼容性前缀
            require('autoprefixer')({ flexbox: 'no-2009' }),
            // 移动端自适应单位可在这里应用px2rem
          ],
        },
      },
    },
    {
      loader: 'less-loader',
      options: {
        lessOptions: {
          // 启用js
          javascriptEnabled: true,
          // 覆盖变量值
          // ant-design 自定义主题可参考 https://ant.design/docs/react/customize-theme
          modifyVars: pkg.theme || {},
        },
      },
    },
  ],
};

module.exports = {
  mode: isProd ? 'production' : 'development',
  // 执行目录
  context,
  // 输出sourcemap
  devtool: useSourceMap && (isProd ? 'source-map' : 'cheap-module-source-map'),
  // 本地开发服务器配置
  devServer: {
    hot: true,
    port: pkg.port || parseInt(process.env.PORT || '3001', 10),
    proxy: pkg.proxy,
    host: '0.0.0.0',
    disableHostCheck: true,
    contentBase: [path.join(context, 'public')],
  },
  // 入口文件
  entry: path.resolve(context, 'src/index.jsx'),
  // 输出配置
  output: {
    filename: `js/[name]${isProd ? '.[chunkhash:7]' : ''}.js`,
    chunkFilename: `js/chunks/[id]${isProd ? '.[chunkhash:7]' : ''}.js`,
    path: outputPath,
  },
  // 模块解析配置
  resolve: {
    alias: {
      '@': path.join(context, 'src'),
    },
    extensions: ['.jsx', '.js', '.json'],
  },
  // 解析规则
  module: {
    rules: [
      {
        oneOf: [
          {
            test: /\.jsx?$/i,
            exclude: /node_modules/,
            include: path.join(context, 'src'),
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            },
          },
          fileLoader,
          styleLoader,
        ],
      },
    ],
  },
  optimization: {
    runtimeChunk: true,
    splitChunks: isProd && {
      chunks: 'all',
      minSize: 1024,
      maxAsyncRequests: 20,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: 10,
        },
        default: {
          minChunks: 2,
          priority: 1,
          reuseExistingChunk: true,
        },
      },
    },
    minimizer: [
      // js迷你化配置
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: env === 'prod',
          },
        },
      }),
      // 用cssnano迷你化样式文件
      new CssMinimizerPlugin(),
    ],
  },
  plugins: [
    // 静态资源压缩 在nginx服务器中设定以下配置生效
    // gzip_static  on;
    // isProd && new CompressionPlugin(),

    // 查看打包后分包文(chunk)件大小，用来分析优化分包和异步加载策略
    process.env.ANALYZE && new BundleAnalyzerPlugin(),

    // 输出css文件
    isProd &&
      new MiniCssExtractPlugin({
        filename: 'css/[name].[chunkhash:7].css',
        chunkFilename: 'css/chunk.[id].[chunkhash:7].css',
      }),

    // 编译前清空输出文件夹
    !isDevServe && new CleanWebpackPlugin(),

    // 定义环境变量，在代码中可从process.env.XXXX取得环境变量
    new webpack.DefinePlugin({
      'process.env': {
        ...appEnv,
        NODE_ENV: JSON.stringify(nodeEnv),
        ENV: JSON.stringify(env),
      },
    }),

    !isProd && new ErrorOverlayPlugin(),
    // html模板
    new HtmlPlugin({
      template: './index.html',
      filename: path.join(outputPath, 'index.html'),
    }),

    // 拷贝静态目录
    (isProd || isDevServe) &&
      new CopyPlugin({
        patterns: [
          {
            from: path.join(context, 'public'),
            to: outputPath,
          },
        ],
      }),

    // 打包
    /*!process.env.ANALYZE &&
      isProd &&
      new ZipPlugin({
        path: process.cwd(),
        filename: zipFilename,
        pathPrefix: bundleName,
      }),*/
  ].filter(Boolean),
};
